#include <iostream>
#include <fstream>
#include <sstream>
#include <deque>
#include <vector>
#include <algorithm>
#include <numeric>

#include <cstdlib>

#include "time_analyzer.h"

struct StackItem
{
  StackItem(const FuncName& name, long long time)
    : func_name(name), time_stamp(time), other_time(0) { }

  FuncName func_name;
  long long time_stamp;
  long long other_time;
};
typedef std::deque<StackItem> StackType;

struct is
{
  is(const FuncName* name): func_name(name) { }
  bool operator()(const StackItem& item) {
    return item.func_name == *func_name;
  }
private:
  const FuncName* func_name;
};

ResultType
time_analyze(const std::string& file_name)
{
  StackType stack;
  ResultType result;

  std::ifstream input(file_name.c_str());
  std::string current_line;
  int counter = 0;
  while (std::getline(input, current_line)) {
    ++counter;
    std::istringstream line(current_line);
    std::string direction(2, ' ');
    FuncName func;
    long long time_stamp;
    line >> direction[0] >> direction[1] >> func >> time_stamp;

    if (direction == "->") {
      if (std::find_if(stack.begin(), stack.end(),
                       is(&func)) == stack.end())
        ++result[func].total_ncalls;
      stack.push_back(StackItem(func, time_stamp));
      ++result[func].cum_ncalls;
    } else if (direction == "<-") {
      if (stack.empty() || stack.back().func_name != func) {
        std::cout << counter << ": WRONG!!!!!!!!!!!!!!!" << std::endl;
        break;
      }
      StackItem top = stack.back();
      stack.pop_back();
      long long time = time_stamp - top.time_stamp;
      result[func].cum_time += time;
      result[func].whole[0] = std::max(result[func].whole[0], (double)time);
      result[func].whole[1] = std::min(result[func].whole[1], (double)time);
      time -= top.other_time;
      result[func].total_time += time;
      result[func].pure[0] = std::max(result[func].pure[0], (double)time);
      result[func].pure[1] = std::min(result[func].pure[1], (double)time);
      if (! stack.empty()) {
        StackItem temp = stack.back();
        temp.other_time += time;
        stack.pop_back();
        stack.push_back(temp);
      }
    } else {
      // This line is not time data, just fall throuth.
    }
  }

  ResultType::iterator it = result.begin();
  for (; it != result.end(); ++it) {
    (*it).second.total_percall = (*it).second.total_time
                               / (double)(*it).second.total_ncalls;
    (*it).second.cum_percall = (*it).second.cum_time
                             / (double)(*it).second.cum_ncalls;
  }

  return result;
}
